
package com.corp.code.generator;

import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.InjectionConfig;
import com.baomidou.mybatisplus.generator.config.*;
import com.baomidou.mybatisplus.generator.config.converts.MySqlTypeConvert;
import com.baomidou.mybatisplus.generator.config.converts.PostgreSqlTypeConvert;
import com.baomidou.mybatisplus.generator.config.po.TableInfo;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import com.corp.common.base.entity.BaseEntity;
import com.google.common.base.Strings;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PropertiesLoaderUtils;

import java.io.IOException;
import java.util.*;

/**
 * Description: 平台代码生成器
 *
 * @author yf
 * @date 2019/7/9
 */
@Data
@Slf4j
public class PlatformCodeGenerator {
    /**
     * 作者
     */
    private String author = null;
    /**
     * 业务模块名称
     */
    private String moduleName = null;
    /**
     * 代码所在服务名
     */
    private String serviceName = "platform-code-generator";
    /**
     * 代码生成的包名
     */
    private String packageName = "com.corp";
    /**
     * resources > mapper > sub directory
     */
    private String mapperSubDir = null;
    /**
     * 代码生成所在项目路径
     */
    private String projectDir;

    /**
     * 需要去掉的表前缀
     */
    private String[] tablePrefix = {"t_"};
    /**
     * 需要生成的表名(两者只能取其一)
     */
    private String[] includeTables = {"t_user"};
    /**
     * 需要排除的表名(两者只能取其一)
     */
    private String[] excludeTables = {};
    /**
     * 是否包含基础业务字段
     */
    private Boolean hasSuperEntity = Boolean.TRUE;
    /**
     * 基础业务字段
     */
    private String[] superEntityColumns = {"id", "is_deleted", "create_time", "update_time"};

    /**
     * 是否启用swagger
     */
    private Boolean isSwagger2 = Boolean.TRUE;

    //数据源
    private String driverName;
    private String driverUrl;
    private String userName;
    private String password;

    public void run() {
        Properties props = getProperties();
        AutoGenerator generator = new AutoGenerator();
        //全局配置
        GlobalConfig gc = new GlobalConfig();
        String outputDir = getOutputDir("/src/main/java");
        if (Strings.isNullOrEmpty(this.getAuthor())) {
            this.setAuthor(props.getProperty("author"));
        }
        gc.setOutputDir(outputDir);
        gc.setAuthor(this.getAuthor());
        gc.setFileOverride(true);
        gc.setOpen(false);
        gc.setActiveRecord(false);
        gc.setEnableCache(false);
        gc.setBaseResultMap(true);
        gc.setBaseColumnList(true);
        gc.setMapperName("%sMapper");
        gc.setXmlName("%sMapper");
        gc.setServiceName("%sService");
        gc.setServiceImplName("%sServiceImpl");
        gc.setControllerName("%sController");
        gc.setSwagger2(isSwagger2);
        generator.setGlobalConfig(gc);
        //数据源配置
        DataSourceConfig dsc = new DataSourceConfig();
        String driverName = "";
        if (StringUtils.isNotEmpty(this.getDriverName()) && StringUtils.isNotEmpty(this.getDriverUrl())) {
            driverName = this.getDriverName();
            dsc.setDriverName(this.getDriverName());
            dsc.setUrl(this.getDriverUrl());
            dsc.setUsername(this.getUserName());
            dsc.setPassword(this.getPassword());
        } else {
            driverName = props.getProperty("spring.datasource.driver-class-name");
            dsc.setDriverName(driverName);
            dsc.setUrl(props.getProperty("spring.datasource.url"));
            dsc.setUsername(props.getProperty("spring.datasource.username"));
            dsc.setPassword(props.getProperty("spring.datasource.password"));
        }
        if (org.apache.commons.lang3.StringUtils.contains(driverName, DbType.MYSQL.getDb())) {
            dsc.setDbType(DbType.MYSQL);
            dsc.setTypeConvert(new MySqlTypeConvert());
        } else {
            dsc.setDbType(DbType.POSTGRE_SQL);
            dsc.setTypeConvert(new PostgreSqlTypeConvert());
        }
        generator.setDataSource(dsc);

        // 策略配置
        StrategyConfig strategy = new StrategyConfig();
        // strategy.setCapitalMode(true);// 全局大写命名
        // strategy.setDbColumnUnderline(true);//全局下划线命名
        strategy.setNaming(NamingStrategy.underline_to_camel);
        strategy.setColumnNaming(NamingStrategy.underline_to_camel);
        strategy.setTablePrefix(tablePrefix);
        if (includeTables.length > 0) {
            strategy.setInclude(includeTables);
        }
        if (excludeTables.length > 0) {
            strategy.setExclude(excludeTables);
        }
        if (hasSuperEntity) {
            strategy.setSuperEntityClass(BaseEntity.class);
            strategy.setSuperEntityColumns(superEntityColumns);
        }
        strategy.setSuperServiceClass("com.baomidou.mybatisplus.extension.service.IService");
        strategy.setSuperServiceImplClass("com.baomidou.mybatisplus.extension.service.impl.ServiceImpl");
        // 自定义 controller 父类
        strategy.setChainModel(false);
        strategy.setEntityLombokModel(true);
        strategy.setControllerMappingHyphenStyle(true);
        generator.setStrategy(strategy);

        // 包配置
        PackageConfig pc = new PackageConfig();
        pc.setModuleName(moduleName);
        pc.setParent(packageName);
//        pc.setController("controller");
//        pc.setEntity("entity");
//        pc.setXml("mapper");
        generator.setPackageInfo(pc);
        //模板配置
        TemplateConfig tc = new TemplateConfig();
        tc.setController("/templates/controller.java.vm");
        tc.setEntity("/templates/entity.java.vm");
        tc.setMapper("/templates/mapper.java.vm");
        tc.setXml(null);
        tc.setService("/templates/service.java.vm");
        tc.setServiceImpl("/templates/serviceImpl.java.vm");
        generator.setTemplate(tc);

        // 注入自定义配置，可以在 VM 中使用 cfg.abc 【可无】
        generator.setCfg(getInjectionConfig());

        generator.execute();
    }

    private InjectionConfig getInjectionConfig() {
        String servicePackage = serviceName.split("-").length > 1 ? serviceName.split("-")[1] : serviceName;
        // 自定义配置
        Map<String, Object> map = new HashMap<>(16);
        InjectionConfig cfg = new InjectionConfig() {
            @Override
            public void initMap() {
                map.put("serviceName", serviceName);
                map.put("servicePackage", servicePackage);
                this.setMap(map);
            }
        };

        List<FileOutConfig> focList = new ArrayList<>();
        focList.add(new FileOutConfig("/templates/mapper.xml.vm") {
            @Override
            public String outputFile(TableInfo tableInfo) {
                map.put("entityKey", tableInfo.getEntityName().toLowerCase());
                String filePath = StrUtil.isBlank(mapperSubDir) ? "/src/main/resources/mapper" : "/src/main/resources/mapper/" + mapperSubDir;
                return getOutputDir(filePath) + "/" + tableInfo.getMapperName() + StringPool.DOT_XML;
            }
        });
        cfg.setFileOutConfigList(focList);
        return cfg;
    }

    /**
     * 获取配置文件
     *
     * @return 配置Props
     */
    private Properties getProperties() {
        // 读取配置文件
        Resource resource = new ClassPathResource("/templates/code.properties");
        Properties props = new Properties();
        try {
            props = PropertiesLoaderUtils.loadProperties(resource);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return props;
    }

    /**
     * 生成到项目中
     *
     * @return outputDir
     */
    public String getOutputDir(String filePath) {
        return (StrUtil.isBlank(projectDir) ? System.getProperty("user.dir") + "/platform-microservices/code-generator" : projectDir) + filePath;
    }

    public static com.corp.code.generator.PlatformCodeGenerator getInstance() {
        return CodeGeneratorHolder.INSTANCE;
    }

    private static class CodeGeneratorHolder {
        private static com.corp.code.generator.PlatformCodeGenerator INSTANCE = new com.corp.code.generator.PlatformCodeGenerator();
    }
}
